/**
 * 
 */
package game;

import java.util.HashSet;
import java.util.Iterator;

/**
 * @author Jean-Baptiste BORIC
 * 
 */
public class Map {
	
	private int width;
	private int height;
	private int[] map;

	private HashSet<Unit> units;
	private HashSet<Building> buildings;
	
	/**
	 * @param width
	 * @param height
	 */
	public Map(int width, int height) {
		super();
		
		if(width < 1 || height < 1) {
			throw new IllegalArgumentException("Invalid dimentions");
		}
		
		this.width = width;
		this.height = height;
		this.map = new int[width*height];
		this.units = new HashSet<Unit>();
		this.buildings = new HashSet<Building>();
	}

	/**
	 * @param units the units to set
	 */
	public void setUnits(HashSet<Unit> units) {
		this.units = units;
	}

	/**
	 * @param buildings the buildings to set
	 */
	public void setBuildings(HashSet<Building> buildings) {
		this.buildings = buildings;
	}

	/**
	 * @return Width of the map
	 */
	public int getWidth() {
		return width;
	}
	
	/**
	 * @return Height of the map
	 */
	public int getHeight() {
		return height;
	}
	
	/**
	 * @return the units
	 */
	public HashSet<Unit> getUnits() {
		return units;
	}

	/**
	 * @return the buildings
	 */
	public HashSet<Building> getBuildings() {
		return buildings;
	}

	/**
	 * Get tile number at position
	 * @param x X position
	 * @param y Y position
	 * @return Tile value
	 */
	public int getTile(int x, int y) {
		if(x < 0 || y < 0 || x >= width || y >= height) {
			throw new IllegalArgumentException("Invalid position for tile");
		}
		return this.map[y*width+x];
	}
	
	public void setTile(int x, int y, int tile) {
		if(x < 0 || y < 0 || x >= width || y >= height) {
			throw new IllegalArgumentException("Invalid position for tile");
		}
		if(tile < 0) {
			throw new IllegalArgumentException("Invalid tile number");
		}
		this.map[y*width+x] = tile;
	}
	
	/**
	 * Place a new Unit on the map
	 * @param unit New Unit
	 */
	public void addUnit(Unit unit) {
		for(Unit i : units) {
			if(i.getX() == unit.getX() && i.getY() == unit.getY()) {
				throw new IllegalStateException("Unit already at position (" +unit.getX()+ "," +unit.getY()+ ")");
			}
		}
		units.add(unit);
	}
	
	/**
	 * Place a new Building on the map
	 * @param buliding
	 */
	public void addBuilding(Building building) {
		for(Building i : buildings) {
			if(i.getX() == building.getX() && i.getY() == building.getY()) {
				throw new IllegalStateException("Building already at position");
			}
		}
		buildings.add(building);
	}
	
	/**
	 * Iterate through all units
	 * @return Iterator
	 */
	public Iterator<Unit> iteratorUnit() {
		return this.units.iterator();
	}
	
	/**
	 * Iterate through all buildings
	 * @return Iterator
	 */
	public Iterator<Building> iteratorBuilding() {
		return this.buildings.iterator();
	}
	
	/**
	 * Remove a Unit by reference
	 * @param unit Unit to remove
	 */
	public void removeUnit(Unit unit) {
		if(this.units.remove(unit) == false) {
			throw new IllegalArgumentException("Remove unregistered unit");
		}
	}
	
	/**
	 * Remove a Unit by reference
	 * @param unit Unit to remove
	 */
	public void removeBuilding(Building building) {
		if(this.buildings.remove(building) == false) {
			throw new IllegalArgumentException("Remove unregistered building");
		}
	}
	
	/**
	 * Get a Unit by coordinates
	 * @param x X position
	 * @param y Y position
	 * @return Unit at position, or null if empty
	 */
	public Unit getUnit(int x, int y) {
		if(x < 0 || y < 0 || x >= width || y >= height) {
			throw new IllegalArgumentException("Invalid position");
		}
		for(Unit i : units) {
			if(i.getX() == x && i.getY() == y) {
				return i;
			}
		}
		return null;
	}

	/**
	 * Get a Building by coordinates
	 * @param x X position
	 * @param y Y position
	 * @return Building at position, or null if empty
	 */
	public Building getBuilding(int x, int y) {
		if(x < 0 || y < 0 || x >= width || y >= height) {
			throw new IllegalArgumentException("Invalid position");
		}
		for(Building i : buildings) {
			if(i.getX() == x && i.getY() == y) {
				return i;
			}
		}
		return null;
	}
	
	public Map resize(int width, int height) {
		// TODO: resize()
		return null;
	}

	@Override
	public String toString() {
		String str = width + "\t" + height + "\t" + buildings.size() + "\t" + units.size() + "\n";
		
		// Write map
		for(int y = 0; y < height; y++) {
			for(int x = 0; x < width; x++) {
				str += getTile(x, y);
				
				if(x != width - 1) {
					str += "\t";
				}
				else {
					str += "\n";
				}
			}
		}
		

		// Write buildings
		for(Building i : this.buildings) {
			str += i + "\n";
		}
		
		// Write units
		for(Unit i : this.units) {
			str += i + "\n";
		}
		
		return str;
	}
}
